package eduware.util;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Vector;

/**
 * 
 * @author Florian Bausch
 *
 */
public class ProcessList extends Thread {
	private HashMap<String, Vector<Integer>> processes = null;
	private int processCount = 0;
	private int refreshInteval;
	
	public ProcessList(int refreshInterval) {
		this.refreshInteval = refreshInterval;
	}

	public void run() {
		while (true) {
			createProcessList();
			try {
				sleep(this.refreshInteval*1000);
			} catch (InterruptedException e) {
				return;
			}
		}
	}

	/**
	 * By executing this method you will get a HashMap that stores all running
	 * processes and their PIDs. Because there can be more than one instances of
	 * one program the <value> of the HashMap is a Vector that stores these
	 * PIDs.
	 * 
	 * @param renewProcessList
	 *            Indicates whether the list of running processes will be
	 *            refreshed while executing this method.
	 * @return A HashMap (String, Vector (Integer))
	 */
	public synchronized HashMap<String, Vector<Integer>> getProcessList(
			boolean renewProcessList) {
		if (renewProcessList)
			createProcessList();
		return this.processes;
	}

	/**
	 * With this method you can find out if there are processes stored.
	 * 
	 * @return <code>true</code>, if there are none stored, <code>false</code>,
	 *         if there are some.
	 */
	public synchronized boolean isNull() {
		return this.processes == null;
	}

	/**
	 * With this method you can find out how many processes are running.
	 * 
	 * @return An <code>int</code> that represents the number of running
	 *         processes.
	 */
	public synchronized int getNumberOfProcesses() {
		return this.processCount;
	}

	/**
	 * Find out if a certain process is running.
	 * 
	 * @param processName
	 *            The exact process name (case sensitive).
	 * @return <code>true</code>, if the process is running, <code>false</code>,
	 *         if not.
	 */
	public synchronized boolean isProcessRunning(String processName) {
		return this.processes.containsKey(processName);
	}

	/**
	 * Get the PIDs to a certain process. Please use <b> isProcessRunning( )</b>
	 * to ensure that there are PIDs available.
	 * 
	 * @param processName
	 *            The name of the process (case sensitive).
	 * @return Vector (Integer) that stores the PIDs.
	 */
	public synchronized Vector<Integer> getPids(String processName) {
		return this.processes.get(processName);
	}

	/**
	 * Creates a list of running processes by using the Windows application
	 * <code>tasklist.exe</code>.
	 */
	public synchronized void createProcessList() {
		int lineCount = 0;
		String pName, pid;
		int processCount = 0;
		String[] splitLine;
		Vector<Integer> v = null;
		HashMap<String, Vector<Integer>> ps = new HashMap<String, Vector<Integer>>();
		try {
			String line;
			// Call the Windows function "tasklist"
			Process p = Runtime.getRuntime().exec(
					System.getenv("windir") + "\\system32\\" + "tasklist.exe");
			// Read the result
			BufferedReader input = new BufferedReader(new InputStreamReader(
					p.getInputStream()));
			// Run through the lines and parse process name and pid
			while ((line = input.readLine()) != null) {
				if (lineCount++ < 3) {
					continue;
				}

				splitLine = line.split("[ ]+");
				int j = -1;
				pName = "";
				while (++j < splitLine.length
						&& !splitLine[j].matches("[0-9]+")) {
					pName += splitLine[j] + " ";
				}
				pid = (j < splitLine.length) ? splitLine[j] : "-1";

				pName = pName.trim();
				// Write into HashMap
				v = (ps.containsKey(pName) ? ps.get(pName)
						: new Vector<Integer>());
				v.add(Integer.valueOf(pid));
				ps.put(pName, v);
				processCount++;
				// Write results to class' attributes
				this.processes = ps;
				this.processCount = processCount;
			}
			input.close();
		} catch (Exception err) {
			err.printStackTrace();
			this.processes = null;
			this.processCount = 0;
		}

	}

	/**
	 * Enables you to kill a windows process when you know the PID.
	 * 
	 * @param pid
	 *            The Windows PID of the process you are about to kill.
	 */
	public synchronized void deleteProcess(int pid) {
		try {
			// Call the Windows function "tasklist"
			Process p = Runtime.getRuntime().exec(
					System.getenv("windir") + "\\system32\\"
							+ "taskkill.exe /pid " + pid);
			// Read the result
			BufferedReader input = new BufferedReader(new InputStreamReader(
					p.getInputStream()));
			input.close();
		} catch (Exception err) {
			err.printStackTrace();
		}

	}
}
